1 module hip.graphics.g2d.particles; 2 import hip.math.vector; 3 import hip.api.graphics.color; 4 5 struct HipParticle 6 { 7 Vector2 initPosition; 8 Vector2 initVelocity; 9 Vector2 initAcceleration; 10 float initAngle = 0; 11 float initScale = 0; 12 float timeStamp = 0; 13 } 14 struct ValueRange 15 { 16 float min = 0, max = 0; 17 18 float rnd() 19 { 20 import hip.math.random; 21 return Random.rangef(min, max); 22 } 23 } 24 25 struct HipParticleSystemConfig 26 { 27 ///Means a variating initial value (random) 28 29 ValueRange scaleInit = ValueRange(1,1); 30 ValueRange scaleEnd = ValueRange(0,0); 31 ValueRange velocityXInit = ValueRange(0,0); 32 ValueRange velocityYInit = ValueRange(0,0); 33 ///In which angle will apply the acceleration 34 ValueRange angleInit = ValueRange(0,0); 35 36 ValueRange accelerationXInit = ValueRange(0,0); 37 ValueRange accelerationYInit = ValueRange(0,0); 38 39 ///In which rotation will init. 40 ValueRange rotationInit = ValueRange(0,0); 41 ///Default color stop is to go from opaque white to transparent white. 42 immutable DefaultParticleColorStops = [HipColorStop(HipColor.white, 0), HipColorStop(HipColor(255,255,255,0), 1)]; 43 44 HipColorStop[] colors = DefaultParticleColorStops; 45 float lifeTime = 2.0; 46 } 47 48 /** 49 * 2D Particle System 50 */ 51 class HipParticleSystem 52 { 53 HipParticle[] particles; 54 HipParticleSystemConfig config; 55 float currentTime = 0; 56 ///How many particles to spawn per second. 57 float emissionRate = 200; 58 ///Will never spawn more than that value 59 uint maxActive = 500; 60 ///Stores how many particles to spawn, accumulates when not integer. 61 protected float spawnAccumulator = 0; 62 ///Particles to iterate 63 protected uint active; 64 65 this(uint maxParticles) 66 { 67 particles = new HipParticle[](maxParticles); 68 active = 0; 69 } 70 71 protected void killParticle(uint id) 72 { 73 assert(id < particles.length, "Particle out of range"); 74 HipParticle temp = particles[id]; 75 --active; 76 particles[id] = particles[active]; 77 particles[active] = temp; 78 } 79 80 struct EmissionZone 81 { 82 ValueRange x, y; 83 } 84 EmissionZone emissionZone; 85 86 void setEmissionZone(int minX, int maxX, int minY, int maxY) 87 { 88 emissionZone = EmissionZone(ValueRange(minX, maxX), ValueRange(minY, maxY)); 89 } 90 91 void spawnParticles(uint count) 92 { 93 uint i = 0; 94 uint newActive = active; 95 ulong max = particles.length; 96 while(i < count && newActive < max) 97 { 98 //Maybe here should have an initialize particle function 99 100 particles[newActive] = HipParticle( 101 Vector2(emissionZone.x.rnd, emissionZone.y.rnd), 102 Vector2(config.velocityXInit.rnd, config.velocityYInit.rnd), 103 Vector2(config.accelerationXInit.rnd, config.accelerationYInit.rnd), 104 config.angleInit.rnd(), 105 config.scaleInit.rnd(), 106 currentTime 107 ); 108 newActive++; 109 i++; 110 } 111 112 active = newActive; 113 } 114 115 void update(float dt) 116 { 117 currentTime+= dt; 118 uint currActive = active; 119 for(uint i = 0; i < currActive; i++) 120 { 121 HipParticle* p = &particles[i]; 122 if(currentTime - p.timeStamp >= config.lifeTime) 123 killParticle(i); 124 } 125 spawnAccumulator+= dt*emissionRate; 126 spawnParticles(cast(uint)spawnAccumulator); 127 spawnAccumulator-= cast(uint)spawnAccumulator; 128 } 129 130 void draw() 131 { 132 import hip.graphics.g2d.renderer2d; 133 import hip.math.utils; 134 float invLifetime = 1.0f / config.lifeTime; 135 for(uint i = 0; i < active; i++) 136 { 137 HipParticle* p = &particles[i]; 138 float t = (currentTime - p.timeStamp) * invLifetime; 139 140 Vector2 partPos = p.initPosition + p.initVelocity*t + p.initAcceleration*0.5*t*t; 141 HipColor partColor = config.colors.gradientColor(t); 142 143 float scale = lerp(p.initScale, config.scaleEnd.max, t); 144 drawTexture(null, cast(int)partPos.x, cast(int)partPos.y, 0, partColor, scale, scale); 145 } 146 } 147 } 148 149 150 class HipParticleSystemDOD 151 { 152 float[] accelerations; 153 float[] velocities; 154 float[] positions; 155 HipColor[] colors; 156 157 }